using System;
using System.Collections.Generic;
using CodePlex.TfsLibrary.ObjectModel;
using CodePlex.TfsLibrary.RepositoryWebSvc;
using Xunit;

namespace CodePlex.TfsLibrary.ClientEngine
{
    public class TfsEngineSyncupTest
    {
        static SourceItem root = SourceItem.FromItem(1, ItemType.Folder,
                                                     SourceItemStatus.Unmodified, SourceItemStatus.Unmodified, @"C:\Foo", null, 1, 0, null,
                                                     SourceItemStatus.Unmodified, "$/Foo", 1, null);

        public class Syncup
        {
            [Fact]
            public void LocalPathMustBeFolder()
            {
                SourceItem callbackItem = null;
                SyncupAction callbackAction = SyncupAction.None;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "This is a file");

                engine.Syncup(@"C:\Foo\bar.txt", null,
                              delegate(SourceItem item,
                                       SyncupAction action,
                                       SourceItemResult result)
                              {
                                  callbackItem = item;
                                  callbackAction = action;
                                  callbackResult = result;
                              });

                Assert.Equal(@"C:\Foo\bar.txt", callbackItem.LocalName);
                Assert.Equal(SyncupAction.None, callbackAction);
                Assert.Equal(SourceItemResult.E_DirectoryNotFound, callbackResult);
            }

            [Fact]
            public void NothingToDo()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "this is my text");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 2, 2, SourceItemStatus.Unmodified);
                engine.StubStatusWithServer(root,
                                            SourceItem.FromItem(2, ItemType.File,
                                                                SourceItemStatus.Unmodified, SourceItemStatus.Unmodified, @"C:\Foo\bar.txt", @"C:\Foo\_tfs\text-base\bar.txt.tfs-base", 2, 0, null,
                                                                SourceItemStatus.Unmodified, "$/Foo/bar.txt", 2, "dt"));

                engine.Syncup(@"C:\Foo",
                              delegate
                              {
                                  throw new InvalidOperationException("Should not be called back");
                              },
                              delegate
                              {
                                  throw new InvalidOperationException("Should not be called back");
                              });
            }

            [Fact]
            public void NotUnderSourceControl()
            {
                SourceItem callbackItem = null;
                SyncupAction callbackAction = SyncupAction.None;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");

                engine.Syncup(@"C:\Foo", null,
                              delegate(SourceItem item,
                                       SyncupAction action,
                                       SourceItemResult result)
                              {
                                  callbackItem = item;
                                  callbackAction = action;
                                  callbackResult = result;
                              });

                Assert.Equal(@"C:\Foo", callbackItem.LocalName);
                Assert.Equal(SyncupAction.None, callbackAction);
                Assert.Equal(SourceItemResult.E_NotUnderSourceControl, callbackResult);
            }

            [Fact]
            public void NullLocalPath()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();

                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         engine.Syncup(null, null, null);
                                                     });
            }

            [Fact]
            public void PathNotFound()
            {
                SourceItem callbackItem = null;
                SyncupAction callbackAction = SyncupAction.None;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();

                engine.Syncup(@"C:\Foo", null,
                              delegate(SourceItem item,
                                       SyncupAction action,
                                       SourceItemResult result)
                              {
                                  callbackItem = item;
                                  callbackAction = action;
                                  callbackResult = result;
                              });

                Assert.Equal(@"C:\Foo", callbackItem.LocalName);
                Assert.Equal(SyncupAction.None, callbackAction);
                Assert.Equal(SourceItemResult.E_DirectoryNotFound, callbackResult);
            }

            [Fact]
            public void ValidatesDirectory()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.StubUpdate();

                engine.Syncup(@"C:\Foo", delegate
                                         {
                                             return false;
                                         },
                              delegate {});

                Assert.Equal(@"C:\Foo", engine.ValidateDirectoryStructure_Directory);
            }
        }

        public class Syncup_Add
        {
            [Fact]
            public void DirectoryAddedWithNewSubItems()
            {
                List<SourceItem> addResults = new List<SourceItem>();
                List<SyncupCallbackResult> syncupResults = new List<SyncupCallbackResult>();
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Baz");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo/Baz", @"C:\Foo\Baz", 1, 1, SourceItemStatus.Add);
                engine.FileSystem.WriteAllText(@"C:\Foo\Baz\biff.txt", "biff");
                engine.StubUpdate();

                engine.Syncup(@"C:\Foo",
                              delegate(SourceItem item)
                              {
                                  addResults.Add(item);
                                  return true;
                              },
                              delegate(SourceItem item,
                                       SyncupAction actionTaken,
                                       SourceItemResult result)
                              {
                                  syncupResults.Add(new SyncupCallbackResult(item, actionTaken, result));
                              });

                Assert.Equal(1, addResults.Count);
                Assert.Equal(@"C:\Foo\Baz\biff.txt", addResults[0].LocalName);
                Assert.Equal(1, syncupResults.Count);
                Assert.Equal(@"C:\Foo\Baz\biff.txt", syncupResults[0].Item.LocalName);
                Assert.Equal(SyncupAction.LocalAdded, syncupResults[0].ActionTaken);
                Assert.Equal(SourceItemResult.S_Ok, syncupResults[0].Result);
            }

            [Fact]
            public void DirectoryWithSubItems()
            {
                List<SourceItem> addResults = new List<SourceItem>();
                List<SyncupCallbackResult> syncupResults = new List<SyncupCallbackResult>();
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Baz");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.FileSystem.WriteAllText(@"C:\Foo\Baz\biff.txt", "biff");
                engine.StubUpdate();

                engine.Syncup(@"C:\Foo",
                              delegate(SourceItem item)
                              {
                                  addResults.Add(item);
                                  return true;
                              },
                              delegate(SourceItem item,
                                       SyncupAction actionTaken,
                                       SourceItemResult result)
                              {
                                  syncupResults.Add(new SyncupCallbackResult(item, actionTaken, result));
                              });

                Assert.Equal(2, addResults.Count);
                Assert.Equal(@"C:\Foo\Baz", addResults[0].LocalName);
                Assert.Equal(@"C:\Foo\Baz\biff.txt", addResults[1].LocalName);
                Assert.Equal(2, syncupResults.Count);
                Assert.Equal(@"C:\Foo\Baz", syncupResults[0].Item.LocalName);
                Assert.Equal(SyncupAction.LocalAdded, syncupResults[0].ActionTaken);
                Assert.Equal(SourceItemResult.S_Ok, syncupResults[0].Result);
                Assert.Equal(@"C:\Foo\Baz\biff.txt", syncupResults[1].Item.LocalName);
                Assert.Equal(SyncupAction.LocalAdded, syncupResults[1].ActionTaken);
                Assert.Equal(SourceItemResult.S_Ok, syncupResults[1].Result);
            }

            [Fact]
            public void DoesNotAddFileAlreadyUnderSourceControl()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "this is my text");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 1, 1, SourceItemStatus.Unmodified);
                engine.StubUpdate();

                engine.Syncup(@"C:\Foo",
                              delegate
                              {
                                  throw new InvalidOperationException("Should not be called back");
                              },
                              delegate
                              {
                                  throw new InvalidOperationException("Should not be called back");
                              });
            }

            [Fact]
            public void DoNotAddMergeFiles()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Baz");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo/Baz", @"C:\Foo\Baz", 1, 1, SourceItemStatus.Add);
                engine.FileSystem.WriteAllText(@"C:\Foo\Baz\biff.txt.mine", "biff");
                engine.FileSystem.WriteAllText(@"C:\Foo\Baz\biff.txt.r123", "biff");
                engine.StubUpdate();

                engine.Syncup(@"C:\Foo",
                              delegate(SourceItem item)
                              {
                                  throw new InvalidOperationException("Should not be called back");
                              },
                              delegate
                              {
                                  throw new InvalidOperationException("Should not be called back");
                              });
            }

            [Fact]
            public void DoNotRecurseIntoDeletedDirectory()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Baz");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo/Baz", @"C:\Foo\Baz", 1, 1, SourceItemStatus.Delete);
                engine.FileSystem.WriteAllText(@"C:\Foo\Baz\biff.txt", "biff");
                engine.StubUpdate();

                engine.Syncup(@"C:\Foo",
                              delegate
                              {
                                  throw new InvalidOperationException("Should not be called back");
                              },
                              delegate
                              {
                                  throw new InvalidOperationException("Should not be called back");
                              });
            }

            [Fact]
            public void DoNotRecurseIntoMissingDirectory()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Baz");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo/Baz", @"C:\Foo\Baz", 1, 1, SourceItemStatus.Unmodified);
                engine.FileSystem.DeleteDirectory(@"C:\Foo\Baz", true);
                engine.StubUpdate();

                engine.Syncup(@"C:\Foo",
                              delegate
                              {
                                  throw new InvalidOperationException("Should not be called back");
                              },
                              delegate {});
            }

            [Fact]
            public void MultipleFileAddWithHierarchy()
            {
                List<SourceItem> addResults = new List<SourceItem>();
                List<SyncupCallbackResult> syncupResults = new List<SyncupCallbackResult>();
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Baz");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "this is my text");
                engine.FileSystem.WriteAllText(@"C:\Foo\unmodified.txt", "unmodified text");
                engine.FileSystem.WriteAllText(@"C:\Foo\Baz\biff.txt", "more text");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo/Baz", @"C:\Foo\Baz", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\unmodified.txt", 1, 1, SourceItemStatus.Unmodified);
                engine.StubUpdate();

                engine.Syncup(@"C:\Foo",
                              delegate(SourceItem item)
                              {
                                  addResults.Add(item);
                                  return true;
                              },
                              delegate(SourceItem item,
                                       SyncupAction actionTaken,
                                       SourceItemResult result)
                              {
                                  syncupResults.Add(new SyncupCallbackResult(item, actionTaken, result));
                              });

                Assert.Equal(2, addResults.Count);
                Assert.Equal(@"C:\Foo\bar.txt", addResults[0].LocalName);
                Assert.Equal(@"C:\Foo\Baz\biff.txt", addResults[1].LocalName);

                Assert.Equal(2, syncupResults.Count);
                Assert.Equal(@"C:\Foo\bar.txt", syncupResults[0].Item.LocalName);
                Assert.Equal(SyncupAction.LocalAdded, syncupResults[0].ActionTaken);
                Assert.Equal(SourceItemResult.S_Ok, syncupResults[0].Result);
                Assert.Equal(@"C:\Foo\Baz\biff.txt", syncupResults[1].Item.LocalName);
                Assert.Equal(SyncupAction.LocalAdded, syncupResults[1].ActionTaken);
                Assert.Equal(SourceItemResult.S_Ok, syncupResults[1].Result);
            }

            [Fact]
            public void NullAddDelegateImpliesTrueReturnValue()
            {
                List<SyncupCallbackResult> syncupResults = new List<SyncupCallbackResult>();
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "this is my text");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.StubUpdate();

                engine.Syncup(@"C:\Foo", null,
                              delegate(SourceItem item,
                                       SyncupAction actionTaken,
                                       SourceItemResult result)
                              {
                                  syncupResults.Add(new SyncupCallbackResult(item, actionTaken, result));
                              });

                Assert.Equal(1, syncupResults.Count);
                Assert.Equal(@"C:\Foo\bar.txt", syncupResults[0].Item.LocalName);
                Assert.Equal(SyncupAction.LocalAdded, syncupResults[0].ActionTaken);
                Assert.Equal(SourceItemResult.S_Ok, syncupResults[0].Result);
            }

            [Fact]
            public void RespectsIgnoreList()
            {
                List<SourceItem> addResults = new List<SourceItem>();
                List<SyncupCallbackResult> syncupResults = new List<SyncupCallbackResult>();
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.IgnoreList.Ignores.Add(@"C:\Foo\bar.txt");
                engine.IgnoreList.Ignores.Add(@"C:\Foo\Blam");
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Baz");
                engine.FileSystem.EnsurePath(@"C:\Foo\Blam");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "this is my text");
                engine.FileSystem.WriteAllText(@"C:\Foo\unmodified.txt", "unmodified text");
                engine.FileSystem.WriteAllText(@"C:\Foo\Baz\biff.txt", "more text");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo/Baz", @"C:\Foo\Baz", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\unmodified.txt", 1, 1, SourceItemStatus.Unmodified);
                engine.StubUpdate();

                engine.Syncup(@"C:\Foo",
                              delegate(SourceItem item)
                              {
                                  addResults.Add(item);
                                  return true;
                              },
                              delegate(SourceItem item,
                                       SyncupAction actionTaken,
                                       SourceItemResult result)
                              {
                                  syncupResults.Add(new SyncupCallbackResult(item, actionTaken, result));
                              });

                Assert.Equal(1, addResults.Count);
                Assert.Equal(@"C:\Foo\Baz\biff.txt", addResults[0].LocalName);

                Assert.Equal(1, syncupResults.Count);
                Assert.Equal(@"C:\Foo\Baz\biff.txt", syncupResults[0].Item.LocalName);
                Assert.Equal(SyncupAction.LocalAdded, syncupResults[0].ActionTaken);
                Assert.Equal(SourceItemResult.S_Ok, syncupResults[0].Result);
            }

            [Fact]
            public void SingleDirectoryAdd()
            {
                List<SourceItem> addResults = new List<SourceItem>();
                List<SyncupCallbackResult> syncupResults = new List<SyncupCallbackResult>();
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Baz");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.StubUpdate();

                engine.Syncup(@"C:\Foo",
                              delegate(SourceItem item)
                              {
                                  addResults.Add(item);
                                  return true;
                              },
                              delegate(SourceItem item,
                                       SyncupAction actionTaken,
                                       SourceItemResult result)
                              {
                                  syncupResults.Add(new SyncupCallbackResult(item, actionTaken, result));
                              });

                Assert.Equal(1, addResults.Count);
                Assert.Equal(@"C:\Foo\Baz", addResults[0].LocalName);

                Assert.Equal(1, syncupResults.Count);
                Assert.Equal(@"C:\Foo\Baz", syncupResults[0].Item.LocalName);
                Assert.Equal(SyncupAction.LocalAdded, syncupResults[0].ActionTaken);
                Assert.Equal(SourceItemResult.S_Ok, syncupResults[0].Result);
            }

            [Fact]
            public void SingleFileAdd()
            {
                List<SourceItem> addResults = new List<SourceItem>();
                List<SyncupCallbackResult> syncupResults = new List<SyncupCallbackResult>();
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "this is my text");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.StubUpdate();

                engine.Syncup(@"C:\Foo",
                              delegate(SourceItem item)
                              {
                                  addResults.Add(item);
                                  return true;
                              },
                              delegate(SourceItem item,
                                       SyncupAction actionTaken,
                                       SourceItemResult result)
                              {
                                  syncupResults.Add(new SyncupCallbackResult(item, actionTaken, result));
                              });

                Assert.Equal(1, addResults.Count);
                Assert.Equal(@"C:\Foo\bar.txt", addResults[0].LocalName);

                Assert.Equal(1, syncupResults.Count);
                Assert.Equal(@"C:\Foo\bar.txt", syncupResults[0].Item.LocalName);
                Assert.Equal(SyncupAction.LocalAdded, syncupResults[0].ActionTaken);
                Assert.Equal(SourceItemResult.S_Ok, syncupResults[0].Result);
            }
        }

        public class Syncup_Delete
        {
            [Fact]
            public void DoesNotRecurseIntoUnversionedFolders()
            {
                List<SyncupCallbackResult> syncupResults = new List<SyncupCallbackResult>();
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Baz");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "this is my text");
                engine.FileSystem.WriteAllText(@"C:\Foo\unmodified.txt", "won't see this");
                engine.FileSystem.WriteAllText(@"C:\Foo\Baz\biff.txt", "this is going away too");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 2, 2, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\unmodified.txt", 2, 2, SourceItemStatus.Unmodified);
                engine.FileSystem.DeleteFile(@"C:\Foo\bar.txt");
                engine.StubUpdate();

                engine.Syncup(@"C:\Foo",
                              delegate
                              {
                                  return false;
                              },
                              delegate(SourceItem item,
                                       SyncupAction actionTaken,
                                       SourceItemResult result)
                              {
                                  syncupResults.Add(new SyncupCallbackResult(item, actionTaken, result));
                              });

                Assert.Equal(1, syncupResults.Count);
                Assert.Equal(@"C:\Foo\bar.txt", syncupResults[0].Item.LocalName);
                Assert.Equal(SyncupAction.LocalDeleted, syncupResults[0].ActionTaken);
                Assert.Equal(SourceItemResult.S_Ok, syncupResults[0].Result);
            }

            [Fact]
            public void MissingFileAfterAddReverts()
            {
                List<SyncupCallbackResult> syncupResults = new List<SyncupCallbackResult>();
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "this is my text");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 2, 2, SourceItemStatus.Add);
                engine.FileSystem.DeleteFile(@"C:\Foo\bar.txt");
                engine.StubUpdate();

                engine.Syncup(@"C:\Foo",
                              delegate
                              {
                                  throw new InvalidOperationException("Should not be called back");
                              },
                              delegate(SourceItem item,
                                       SyncupAction actionTaken,
                                       SourceItemResult result)
                              {
                                  syncupResults.Add(new SyncupCallbackResult(item, actionTaken, result));
                              });

                Assert.Equal(1, syncupResults.Count);
                Assert.Equal(@"C:\Foo\bar.txt", syncupResults[0].Item.LocalName);
                Assert.Equal(SyncupAction.LocalReverted, syncupResults[0].ActionTaken);
                Assert.Equal(SourceItemResult.S_Ok, syncupResults[0].Result);
                Assert.False(engine.FileSystem.FileExists(@"C:\Foo\bar.txt"));
            }

            [Fact]
            public void MissingFolder()
            {
                List<SyncupCallbackResult> syncupResults = new List<SyncupCallbackResult>();
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Baz");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "this is my text");
                engine.FileSystem.WriteAllText(@"C:\Foo\unmodified.txt", "won't see this");
                engine.FileSystem.WriteAllText(@"C:\Foo\Baz\biff.txt", "this is going away too");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo/Baz", @"C:\Foo\Baz", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 2, 2, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\unmodified.txt", 2, 2, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\Baz\biff.txt", 2, 2, SourceItemStatus.Unmodified);
                engine.FileSystem.DeleteFile(@"C:\Foo\bar.txt");
                engine.FileSystem.DeleteDirectory(@"C:\Foo\Baz", true);
                engine.StubUpdate();

                engine.Syncup(@"C:\Foo",
                              delegate
                              {
                                  throw new InvalidOperationException("Should not be called back");
                              },
                              delegate(SourceItem item,
                                       SyncupAction actionTaken,
                                       SourceItemResult result)
                              {
                                  syncupResults.Add(new SyncupCallbackResult(item, actionTaken, result));
                              });

                Assert.Equal(3, syncupResults.Count);
                Assert.Equal(@"C:\Foo\bar.txt", syncupResults[0].Item.LocalName);
                Assert.Equal(SyncupAction.LocalDeleted, syncupResults[0].ActionTaken);
                Assert.Equal(SourceItemResult.S_Ok, syncupResults[0].Result);
                Assert.Equal(@"C:\Foo\Baz\biff.txt", syncupResults[1].Item.LocalName);
                Assert.Equal(SyncupAction.LocalDeleted, syncupResults[1].ActionTaken);
                Assert.Equal(SourceItemResult.S_Ok, syncupResults[1].Result);
                Assert.Equal(@"C:\Foo\Baz", syncupResults[2].Item.LocalName);
                Assert.Equal(SyncupAction.LocalDeleted, syncupResults[2].ActionTaken);
                Assert.Equal(SourceItemResult.S_Ok, syncupResults[2].Result);
            }

            [Fact]
            public void MultipleDeleteWithHierarchy()
            {
                List<SyncupCallbackResult> syncupResults = new List<SyncupCallbackResult>();
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Baz");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "this is my text");
                engine.FileSystem.WriteAllText(@"C:\Foo\unmodified.txt", "won't see this");
                engine.FileSystem.WriteAllText(@"C:\Foo\Baz\biff.txt", "this is going away too");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo/Baz", @"C:\Foo\Baz", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 2, 2, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\unmodified.txt", 2, 2, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\Baz\biff.txt", 2, 2, SourceItemStatus.Unmodified);
                engine.FileSystem.DeleteFile(@"C:\Foo\bar.txt");
                engine.FileSystem.DeleteFile(@"C:\Foo\Baz\biff.txt");
                engine.StubUpdate();

                engine.Syncup(@"C:\Foo",
                              delegate
                              {
                                  throw new InvalidOperationException("Should not be called back");
                              },
                              delegate(SourceItem item,
                                       SyncupAction actionTaken,
                                       SourceItemResult result)
                              {
                                  syncupResults.Add(new SyncupCallbackResult(item, actionTaken, result));
                              });

                Assert.Equal(2, syncupResults.Count);
                Assert.Equal(@"C:\Foo\bar.txt", syncupResults[0].Item.LocalName);
                Assert.Equal(SyncupAction.LocalDeleted, syncupResults[0].ActionTaken);
                Assert.Equal(SourceItemResult.S_Ok, syncupResults[0].Result);
                Assert.Equal(@"C:\Foo\Baz\biff.txt", syncupResults[1].Item.LocalName);
                Assert.Equal(SyncupAction.LocalDeleted, syncupResults[1].ActionTaken);
                Assert.Equal(SourceItemResult.S_Ok, syncupResults[1].Result);
            }

            [Fact]
            public void RecurseIntoAddedFolders()
            {
                List<SyncupCallbackResult> syncupResults = new List<SyncupCallbackResult>();
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Baz");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "this is my text");
                engine.FileSystem.WriteAllText(@"C:\Foo\unmodified.txt", "won't see this");
                engine.FileSystem.WriteAllText(@"C:\Foo\Baz\biff.txt", "this is going away too");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo/Baz", @"C:\Foo\Baz", 1, 1, SourceItemStatus.Add);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 2, 2, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\unmodified.txt", 2, 2, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\Baz\biff.txt", 2, 3, SourceItemStatus.Add);
                engine.FileSystem.DeleteFile(@"C:\Foo\Baz\biff.txt");
                engine.StubUpdate();

                engine.Syncup(@"C:\Foo",
                              delegate
                              {
                                  throw new InvalidOperationException("Should not be called back");
                              },
                              delegate(SourceItem item,
                                       SyncupAction actionTaken,
                                       SourceItemResult result)
                              {
                                  syncupResults.Add(new SyncupCallbackResult(item, actionTaken, result));
                              });

                Assert.Equal(1, syncupResults.Count);
                Assert.Equal(@"C:\Foo\Baz\biff.txt", syncupResults[0].Item.LocalName);
                Assert.Equal(SyncupAction.LocalReverted, syncupResults[0].ActionTaken);
                Assert.Equal(SourceItemResult.S_Ok, syncupResults[0].Result);
            }

            [Fact]
            public void SingleDelete()
            {
                List<SyncupCallbackResult> syncupResults = new List<SyncupCallbackResult>();
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "this is my text");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 2, 2, SourceItemStatus.Unmodified);
                engine.FileSystem.DeleteFile(@"C:\Foo\bar.txt");
                engine.StubUpdate();

                engine.Syncup(@"C:\Foo",
                              delegate
                              {
                                  throw new InvalidOperationException("Should not be called back");
                              },
                              delegate(SourceItem item,
                                       SyncupAction actionTaken,
                                       SourceItemResult result)
                              {
                                  syncupResults.Add(new SyncupCallbackResult(item, actionTaken, result));
                              });

                Assert.Equal(1, syncupResults.Count);
                Assert.Equal(@"C:\Foo\bar.txt", syncupResults[0].Item.LocalName);
                Assert.Equal(SyncupAction.LocalDeleted, syncupResults[0].ActionTaken);
                Assert.Equal(SourceItemResult.S_Ok, syncupResults[0].Result);
            }
        }

        public class Syncup_Update
        {
            [Fact]
            public void RunsUpdate()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "this is my text");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 2, 2, SourceItemStatus.Unmodified);
                engine.StubUpdate();

                engine.Syncup(@"C:\Foo", null, null);

                Assert.Equal(@"C:\Foo", engine.Update_LocalPath);
                Assert.True(engine.Update_Recursive);
                Assert.Equal<VersionSpec>(VersionSpec.Latest, engine.Update_Version);
            }
        }

        class SyncupCallbackResult
        {
            public readonly SyncupAction ActionTaken;
            public readonly SourceItem Item;
            public readonly SourceItemResult Result;

            public SyncupCallbackResult(SourceItem item,
                                        SyncupAction actionTaken,
                                        SourceItemResult result)
            {
                Item = item;
                ActionTaken = actionTaken;
                Result = result;
            }
        }
    }
}